package archer.framework.test.support.starter;

import archer.framework.test.support.TestConfig;
import archer.framework.utils.ProfileUtils;
import jodd.typeconverter.Convert;
import jodd.util.StringUtil;
import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.nio.SelectChannelConnector;
import org.eclipse.jetty.webapp.WebAppClassLoader;
import org.eclipse.jetty.webapp.WebAppContext;

import java.util.ArrayList;
import java.util.List;

/**
 * 抽象启动类
 *
 * @author christ
 * @date 6/11/2015
 */
public abstract class AbstractStarter extends TestConfig {

    public static final String[] TLD_JAR_NAMES = new String[]{"spring-webmvc", "shiro-web",};

    /**
     * 定义profile
     */
    public abstract String defineProfile();


    /**
     * 定义port
     *
     * @return
     */
    public int definePort() {
        return Convert.toIntValue(getConfig("test.starter.port"));
    }

    /**
     * 定义context
     *
     * @return
     */
    public String defineContext() {
        return getConfig("test.starter.context");
    }

    public void run() {

        ProfileUtils.setProfilePlaceHolder(defineProfile());


        // 启动Jetty
        Server server = JettyFactory.createServerInSource(definePort(), defineContext());
        JettyFactory.setTldJarNames(server, TLD_JAR_NAMES);

        beforeStart();

        try {
            server.start();

            System.out.println("[INFO] Server running at http://localhost:" + definePort() + defineContext());
            System.out.println("[HINT] Hit Enter to reload the application quickly");

            // 等待用户输入回车重载应用.
            while (true) {
                char c = (char) System.in.read();
                if (c == '\n') {
                    JettyFactory.reloadContext(server);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
            System.exit(-1);
        }
    }

    public void beforeStart(){

    }


    /**
     * 创建Jetty Server的工厂类.
     *
     * @author calvin
     */
    public static class JettyFactory {

        public static String DEFAULT_WEBAPP_PATH = "src/main/webapp";
        public static String WINDOWS_WEBDEFAULT_PATH = "jetty/webdefault-windows.xml";

        /**
         * 创建用于开发运行调试的Jetty Server, 以src/main/webapp为Web应用目录.
         */
        public static Server createServerInSource(int port, String contextPath) {
            Server server = new Server();
            // 设置在JVM退出时关闭Jetty的钩子。
            server.setStopAtShutdown(true);

            SelectChannelConnector connector = new SelectChannelConnector();
            connector.setPort(port);
            // 解决Windows下重复启动Jetty居然不报告端口冲突的问题.
            connector.setReuseAddress(false);
            server.setConnectors(new Connector[]{connector});

            WebAppContext webContext = new WebAppContext(DEFAULT_WEBAPP_PATH, contextPath);
            // 修改webdefault.xml，解决Windows下Jetty Lock住静态文件的问题.
            webContext.setDefaultsDescriptor(WINDOWS_WEBDEFAULT_PATH);
            server.setHandler(webContext);

            return server;
        }

        /**
         * 设置除jstl-*.jar外其他含tld文件的jar包的名称.
         * jar名称不需要版本号，如sitemesh, shiro-web
         */
        public static void setTldJarNames(Server server, String... jarNames) {
            WebAppContext context = (WebAppContext) server.getHandler();
            List<String> jarNameExprssions = new ArrayList<String>();
            jarNameExprssions.add(".*/jstl-[^/]*\\.jar$");
            jarNameExprssions.add(".*/.*taglibs[^/]*\\.jar$");
            for (String jarName : jarNames) {
                jarNameExprssions.add(".*/" + jarName + "-[^/]*\\.jar$");
            }

            context.setAttribute("org.eclipse.jetty.server.webapp.ContainerIncludeJarPattern",
                    StringUtil.join(jarNameExprssions.toArray(new String[0]), '|'));

        }

        /**
         * 快速重新启动application，重载target/classes与target/test-classes.
         */
        public static void reloadContext(Server server) throws Exception {
            WebAppContext context = (WebAppContext) server.getHandler();

            System.out.println("[INFO] Application reloading");
            context.stop();

            WebAppClassLoader classLoader = new WebAppClassLoader(context);
            classLoader.addClassPath("target/classes");
            classLoader.addClassPath("target/test-classes");
            context.setClassLoader(classLoader);

            context.start();

            System.out.println("[INFO] Application reloaded");
        }
    }
}
